/*
 * Copyright (c) 1997 - 2012 Rakuten, Inc. All Rights Reserved.
 * $Id$
 * Created on 2012/05/24
 */
package com.rakuten.api.mule.model.item;

import java.util.AbstractList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import org.codehaus.jackson.annotate.JsonAutoDetect;
import org.codehaus.jackson.annotate.JsonIgnoreProperties;
import org.codehaus.jackson.map.annotate.JsonSerialize;
/**
 *
 * @author hayatsu
 */
@JsonAutoDetect
@JsonIgnoreProperties(ignoreUnknown = true)
@JsonSerialize(include=JsonSerialize.Inclusion.NON_NULL)
public class MultiLang implements java.io.Serializable{

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;

	public static final String KEY_DEFAULT_VALUE="value";

	private String defVal;
	private Map<Locale, String> localizations;

	public MultiLang(){
	}

	public MultiLang(String defaultValue){
		this.defVal=defaultValue;
	}

	/**
	 * Returns the default value;
	 */
	public String getValue(){
		return defVal;
	}
	/**
	 * Sets the default value.
	 */
	public void setValue(String value){
		this.defVal=value;
	}
	/**
	 * Returns the string associated with the specified locale, or the default value if not found.
	 * If the argument is <code>null</code> or a ROOT locale, this
	 * method returns the default value({@link #getValue()}).
	 */
	public String get(Locale locale){
		if(locale==null||localizations==null||locale.equals(Locale.ROOT))
			return getValue();
		String ret=localizations.get(locale);//TODO: have to loop here
		return ret==null?getValue():ret;
	}
	public String getWithoutFallback(Locale locale){
		if(locale==null||locale.equals(Locale.ROOT))
			return getValue();
		return localizations==null?null:localizations.get(locale);
	}
	/**
	 * Sets a localized string, or, the default value if <code>locale</code> is
	 * either <code>null</code> or ROOT.
	 */
	public void set(Locale locale, String value){
		if(locale==null||locale.equals(Locale.ROOT)){
			setValue(value);
		}else{
			if(localizations==null) localizations=new java.util.HashMap<>(4);
			if(value==null) localizations.remove(locale);
			else localizations.put(locale, value);
		}
	}

	/**
	 * The locales currently stored within this object.
	 * The return value can be used for removal, but cannot be added anything.
	 */
	public Set<Locale> locales(){
		if(localizations==null) return Collections.emptySet();
		return localizations.keySet();
	}
	/**
	 * An iterable that first returns a ROOT locale(corresponding to the default value),
	 * and then the results of any other locales currently stored in this object.
	 */
	public Iterable<Locale> localesWithRoot(){
		return new Iterable<Locale>(){
			@Override
			public Iterator<Locale> iterator(){
				return new Iterator<Locale>(){
					Iterator<Locale> locales;
					@Override
					public boolean hasNext(){
						if(locales==null) return true;
						return locales.hasNext();
					}
					@Override
					public Locale next(){
						if(locales==null){
							if(localizations==null) locales=Collections.emptyIterator();
							else locales=localizations.keySet().iterator();
							return Locale.ROOT;
						}
						return locales.next();
					}
					@Override
					public void remove(){
						throw new UnsupportedOperationException();
					}
				};
			}
		};
	}

	//TODO
	//public String bestMatch(Locale locale){
	//}

	/**
	 * Convenience method for extracting a value from a possibly <code>null</code>
	 * MultiLang instance.  Useful for doing stuff like <code>MultiLang.valueOf(item.getName())</code>
	 * and not worry for null pointers.
	 */
	public static String valueOf(MultiLang lang, Locale locale){
		return lang==null?null:lang.get(locale);
	}
	/**
	 * Convenience method for extracting a value from a possibly <code>null</code>
	 * MultiLang instance.  Useful for doing stuff like <code>MultiLang.valueOf(item.getName())</code>
	 * and not worry for null pointers.
	 */
	public static String valueOf(MultiLang lang){
		return lang==null?null:lang.getValue();
	}

	public static String toString(Iterable<MultiLang> langs){
		return toString(langs, null);
	}
	public static String toString(Iterable<MultiLang> langs, Locale locale){
		if(langs==null) return "null";
		StringBuilder sb=new StringBuilder().append('[');
		for(MultiLang ml : langs){
			sb.append(valueOf(ml, locale)).append(", ");
		}
		if(sb.length()>1) sb.setLength(sb.length() - 2);//drop the last delimiter with space
		return sb.append(']').toString();
	}

	public static List<String> toPlainString(final List<MultiLang> multiLangList){
		return toPlainString(multiLangList, null);
	}
	/**
	 * Returns a read-only view of a <code>MultiLang</code> list as a plain <code>String</code> list.
	 */
	public static List<String> toPlainString(final List<MultiLang> multiLangList, final Locale locale){
		if(multiLangList==null) return null;
		return new AbstractList<String>(){
			@Override
			public String get(int index){
				return valueOf(multiLangList.get(index), locale);
			}
			@Override
			public int size(){
				return multiLangList.size();
			}
		};
	}
}
